﻿#region

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SharpObservation.Tests.Dummy;
using Desc = Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute;
#endregion


// The whole week delegate thing needs a rethink!!!
// Weakeness should be established on the event += operator

namespace SharpObservation.Tests
{
    /// <summary>
    ///   Summary description for DelegateTests
    /// </summary>
    [TestClass]
    public class DelegateTests
    {
        ///<summary>
        ///  Gets or sets the test context which provides
        ///  information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }

        #region Additional test attributes

        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //

        #endregion

        [TestMethod]
        [Desc("Confirms that weak events provide notifications on the correct syncornization context")]
        public void TestNotificationsOnSyncContext()
        {
            var threads = new Thread[3];           // [0] = current thread, [1] = dummyHandlerThread, [2] = delegate invoke thread
            threads[0] = Thread.CurrentThread;

            using (var ct = new CancellationTokenSource())
            {
                // define a delegate which captures the thread it is running on.
                EventHandler<EventArgs> dummyHandler = (sender, e) =>
                {
                    threads[1] = Thread.CurrentThread;
                    ct.Cancel();
                };

                using (var m = new MockSynchronizationContext{Timeout = new TimeSpan(0,0,10)})
                {
                    var syncHandler = dummyHandler.AsAsync();

                    // schedule the delegate to be invoked from a different thread
                    ThreadPool.QueueUserWorkItem( state =>
                    {
                        threads[2] = Thread.CurrentThread;
                        syncHandler(this, EventArgs.Empty);
                    });

                    // Run our mock SyncContext so that the delegate can be invoked on this thread.
                    m.Run(ct);
                }
            }

            Assert.AreSame(threads[0], threads[1], "AsSync not invoking delegate via Synchronization Context");
            
            if(threads[0] == threads[2])
                Assert.Inconclusive("Worker thread same as calling thread");
        }

        //[TestMethod]
        //[Desc("Confirms that weak delegates are implemented correctly")]
        //public void TestWeakDelegates()
        //{
        //    const int maxNotifiers = 500;
        //    const int maxInstances = 1000;
        //    var counter = new FireCounter();
        //    var dummies = new LifetimeDummy[1 + maxInstances];
        //    var notifiers = new NotifyDummy[1 + maxNotifiers];

        //    for (var i = 0; i < maxNotifiers; i++)
        //        notifiers[i] = new NotifyDummy();

        //    for (var i = 0; i < maxInstances; i++)
        //    {
        //        // Set up a dummy to update our counters...
        //        var lifetimer = dummies[i] = new LifetimeDummy();
        //        lifetimer.PropertyChanging += (o, e) => counter.ChangingFirings++;
        //        lifetimer.PropertyChanged += (o, e) =>
        //                                         {
        //                                             counter.ChangedFirings++;
        //                                             Assert.AreEqual("Name", e.PropertyName);
        //                                         };
        //        lifetimer.Disposed += (o, e) => counter.DisposedFirings++;
        //    }

        //    for (var i = 0; i < maxInstances; i++)
        //    {
        //        var lifetimer = dummies[i];
        //        var notifier = notifiers[i % maxNotifiers];

        //        var propertyChangedHandler = new PropertyChangedEventHandler(lifetimer.OnPropertyChanged);
        //        propertyChangedHandler = propertyChangedHandler.AsWeak((o, x) => notifier.PropertyChanged -= x);
        //        notifier.PropertyChanged += propertyChangedHandler;

        //        var propertyChangingHandler = new PropertyChangingEventHandler(lifetimer.OnPropertyChanging);
        //        propertyChangingHandler = propertyChangingHandler.MakeWeak(x => notifier.PropertyChanging -= x);
        //        notifier.PropertyChanging += propertyChangingHandler;
        //    }

        //    var r = new Random();

        //    // Trigger a change notification
        //    for (int i = 0; i < maxNotifiers; i++)
        //        notifiers[i].Name = r.NextDouble().ToString();

        //    // Dispose of all instances
        //    for (var i = 0; i < maxInstances; i++)
        //        dummies[i].Dispose();

        //    // Trigger a change notification
        //    for (var i = 0; i < maxNotifiers; i++)
        //        notifiers[i].Name = r.NextDouble().ToString();

        //    // We must have been notified     
        //    Assert.IsTrue(maxInstances <= counter.ChangingFirings, "PropertyChanging Notifications are not being performed");
        //    Assert.IsTrue(maxInstances <= counter.ChangedFirings, "PropertyChanged Notifications are not being performed");

        //    // Ensure that we do have any strong references (if nothing has been collected, then we're in trouble)
        //    Assert.AreEqual(maxInstances, counter.DisposedFirings);

        //    // Ensure that we do have any strong references (if nothing has been collected, then we're in trouble)
        //    for (var instances = 0; instances < maxNotifiers; instances++)
        //        Assert.IsFalse(notifiers[instances].IsAnyEventAttached);
        //}

        //[TestMethod]
        //public void StressTestWeakDelegates()
        //{
        //    var actionCount = 10;
        //    var actions = new EventHandler[actionCount];
        //    var executions = 0;
        //    var deregisters = 0;
        //    for (int i = 0; i < actionCount; i++)
        //    {
        //        EventHandler dummyHandler = (o, e) => { executions++; };
        //        actions[i] = WeakDelegate.MakeWeak< EventHandler>(dummyHandler);
        //    }

        //    for (int i = 0; i < actions.Length - 1; i++)
        //        actions[i](null, null);

        //    Assert.IsTrue((executions + deregisters) == actionCount);

        //    GC.Collect();

        //    Assert.IsTrue(deregisters > 0);
        //}

        //[TestMethod]
        //[Desc("Confirms that Delegate.IsWeak() extension returns accurate results")]
        //public void TestIsWeakMethod()
        //{
        //    // Anonyous methods which don't access *this* are declared static by the compile, and are never made weak.
        //    // We invoke this.GetType to force the delegate to by instance bound.
        //    EventHandler dummyHandler = (o, e) => { var type = this.GetType(); };

        //    var newHandler = WeakDelegate.MakeWeak(dummyHandler);

        //    Assert.IsFalse(dummyHandler.IsWeak());
        //    Assert.IsTrue(newHandler.IsWeak());
        //}


        //private int counter;

        //[TestMethod]
        //[Desc("Tests whether weak delegates can have a return type")]
        //public void TestWeakLambdas()
        //{
        //    // Anonyous methods which don't access *this* are declared static by the compile, and are never made weak.
        //    // We invoke this.GetType to force the delegate to by instance bound.
        //    Action<int> setCounter = i => counter = i;

        //    var weakIncreaseByOne = WeakDelegate.MakeWeak(setCounter);

        //    counter = 0;
        //    setCounter(1);
        //    var expected = counter;

        //    counter = 0;
        //    weakIncreaseByOne(1);
        //    var actual = counter;

        //    Assert.AreEqual(expected, actual);
        //}


        //[TestMethod, ExpectedException(typeof(GenericArgumentException))]
        //[Desc("Ensures that delegates with return values are actively rejected")]
        //public void EnsureDelegateWithReturnTypesAreRejected()
        //{
        //    // Anonyous methods which don't access *this* are declared static by the compile, and are never made weak.
        //    // We invoke this.GetType to force the delegate to by instance bound.
        //    Func<int, int> increaseByOne = i =>
        //    {
        //        var type = this.GetType();
        //        return i + 1;
        //    };
        //    var weakIncreaseByOne = WeakDelegate.MakeWeak(increaseByOne);
        //    weakIncreaseByOne(1);
        //}

        //[TestMethod]
        //[Desc("Ensures that static delegates bypass weak referencing without errors")]
        //public void EnsureStaticDelegateIsUnchanged()
        //{
        //    Action<int> setCounter = i => { };
        //    var weakIncreaseByOne = WeakDelegate.MakeWeak(setCounter);
        //    Assert.AreSame(setCounter, weakIncreaseByOne);
        //}


        //[TestMethod]
        //[Desc("Ensures that weak delegates bypass weak referencing without errors")]
        //public void EnsureWeakDelegateIsUnchanged()
        //{
        //    Action<int> staticDelegate = i => { };
        //    var weakDelegate = WeakDelegate.MakeWeak(staticDelegate);
        //    var weakDelegate2 = WeakDelegate.MakeWeak(weakDelegate);
        //    Assert.AreSame(weakDelegate, weakDelegate2);
        //}   
    }
}